﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using Public;
using System.Net;
using System.Windows.Controls;
using System.Windows.Data;
using System.IO;
using System.Security.Cryptography;

namespace ControlCenterController
{

    delegate void TargetChannelClosedHandler(TargetItem sender);

    class TargetItem : ListBoxItem
    {
        public TargetItem(Target target, TargetChannelClosedHandler targetChannelClosedHandler)
        {
            this.target = target;
            this.onTargetChannelClosed += targetChannelClosedHandler;
            target.onChannelClosed += new Action(target_onChannelClosed);

            StackPanel panel=new StackPanel();
            panel.Margin = new System.Windows.Thickness(0);
            panel.Orientation=Orientation.Vertical;
            TextBlock header=new TextBlock();
            header.Margin=new System.Windows.Thickness(0);
            header.Padding=new System.Windows.Thickness(0);
            header.Text=target.MachineName;
            TextBlock ip=new TextBlock();
            ip.Margin = new System.Windows.Thickness(0);
            ip.Padding = new System.Windows.Thickness(0);
            ip.Text = target.IPAddress.ToString();
            TextBlock status = new TextBlock();
            status.Margin = new System.Windows.Thickness(0);
            status.Padding = new System.Windows.Thickness(0);
            status.Text = target.Status;
            panel.Children.Add(header);
            panel.Children.Add(ip);
            panel.Children.Add(status);
            CheckBox checkBox = new CheckBox();
            checkBox.Content = panel;

            Binding binding = new Binding();
            binding.Source = checkBox;
            binding.Path = new System.Windows.PropertyPath("IsChecked");
            binding.Mode = BindingMode.TwoWay;
            this.SetBinding(ListBoxItem.IsSelectedProperty, binding);

            this.Content = checkBox;
            
        }

        private Target target;
        public Target Target { get { return target; } }

        private event TargetChannelClosedHandler onTargetChannelClosed;

        void target_onChannelClosed()
        {
            this.onTargetChannelClosed(this);
        }
    }

    class Target
    {
        private Target(string machineName, IPAddress ipAddress, IService service,ChannelFactory<IService> channel)
        {
            this.machineName = machineName;
            this.ipAddress = ipAddress;
            this.service = service;
            this.Status = "Available";
            channel.Closed += new EventHandler(channel_Closed);
            channel.Faulted += new EventHandler(channel_Faulted);
            this.channel = channel;
        }

        void channel_Faulted(object sender, EventArgs e)
        {
            this.CloseChannel();
        }

        public event Action onChannelClosed;

        void channel_Closed(object sender, EventArgs e)
        {
            this.onChannelClosed();
        }

        public static Target Connect(System.Net.IPAddress ipAddress)
        {
            try
            {
                NetTcpBinding binding = new NetTcpBinding(SecurityMode.None);
                binding.OpenTimeout = new TimeSpan(0, 1, 0);
                binding.SendTimeout = new TimeSpan(0, 1, 0);
                binding.ReceiveTimeout = new TimeSpan(0, 1, 0);

                ChannelFactory<IService> channel = new ChannelFactory<IService>(binding, new EndpointAddress(new Uri("net.tcp://" + ipAddress.ToString() + ":915/ControlCenterService")));
                IService service = channel.CreateChannel();
                
                return new Target(service.GetMachineName(), ipAddress, service,channel);
            }
            catch
            {
                return null;
            }
            
        }

        public static Target Connect(Byte[] ipAddressByte)
        {
            return Connect(new IPAddress(ipAddressByte));
        }

        public void CloseChannel()
        {
            try
            {
                channel.Close();
            }
            catch { }
        }

        private ChannelFactory<IService> channel;

        public string Status { get; set; }

        private string machineName;
        public string MachineName { get { return machineName; } }

        private System.Net.IPAddress ipAddress;
        public System.Net.IPAddress IPAddress { get { return ipAddress; } }

        private IService service;

        public Result SendFiles(List<string> files, string path)
        {
            List<Exception> exceptions = new List<Exception>();
            List<FileWrapper> fileWrappers = new List<FileWrapper>();
            foreach (string fileName in files)
            {
                FileWrapper fileWrapper;
                try
                {
                    fileWrapper = new FileWrapper(path + System.IO.Path.GetFileName(fileName), new FileStream(fileName, FileMode.Open));
                    try
                    {
                        service.SendFile(fileWrapper);
                        fileWrapper.FileStream.Close();
                        FileStream fileStream = new FileStream(fileName, FileMode.Open);
                        byte[] data = MD5.Create().ComputeHash(fileStream);
                        StringBuilder stringBuilder = new StringBuilder();
                        for (int j = 0; j < data.Length; j++)
                            stringBuilder.Append(data[j].ToString("x2"));
                        fileStream.Close();
                        if (!service.CheckFile(fileWrapper.FilePath, stringBuilder.ToString()))
                            throw new Exception("The file appeared to be wrong or unaccessable during file checking.");
                    }
                    catch (Exception e)
                    {
                        fileWrapper.FileStream.Close();
                        exceptions.Add(e);
                    }
                }
                catch (Exception ex)
                {
                    exceptions.Add(new Exception("Failed to open file:", ex));
                }
            }
            if (exceptions.Count == 0)
                return new Result("All the files have been sent successfully");
            else
                return new Result("Exception is thrown during sending files, " + (fileWrappers.Count - exceptions.Count).ToString() + " of " + fileWrappers.Count + " succeed.", exceptions);
        }

        public Result RunProgram(string targetProgramPath,string arguments)
        {
            try
            {
                return service.RunProgram(targetProgramPath, arguments);
            }
            catch(Exception e)
            {
                return new Result("Exception is thrown during running a program.", e);
            }
        }

        public Result RunTerminalScript(List<string> scriptLines)
        {
            try
            {
                return service.RunTerminalScript(scriptLines);
            }
            catch(Exception e)
            {
                return new Result("Exception is thrown during running terminal script.", e);
            }
        }

        public Result KillAllControlledPrograms()
        {
            try
            {
                service.KillAllControlledPrograms();
                return new Result("The controlled programs has been killed successfully.");
            }
            catch(Exception e)
            {
                return new Result("Exception is thrown during killing the programs.", e);
            }
        }
    }
}
